package at.rtm.service.extractor;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringWriter;

/**
 * This extractor extracts information based on a xpath query.
 *
 * @param <TYPE>
 */
public class XPathBasedExtractor<TYPE> implements Extractor<TYPE> {
    private Logger logger = Logger.getLogger(XPathBasedExtractor.class);
    private Tidy tidy;
    private XPathExpression expr;
    private XPath xpath;
    private String xpathQuery;

    private ConversionService conversionService;

    @Autowired
    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public XPathBasedExtractor(String xpathQuery) {
        this.xpathQuery = xpathQuery;
        initTidy();
        initXPath();
    }

    private void initXPath() {
        xpath = XPathFactory.newInstance().newXPath();
        try {
            expr = xpath.compile(xpathQuery);
        } catch (XPathExpressionException e) {
            throw new IllegalStateException("xpath expression couldn't be compiled", e);
        }
    }

    private void initTidy() {
        tidy = new Tidy();
        tidy.setShowWarnings(false);
        tidy.setShowErrors(0);
        tidy.setQuiet(true);
        tidy.setMakeClean(true);
    }

    @Override
    public TYPE extractInformation(Resource resource, Class<TYPE> clazz) {
        try {
            Document doc = tidy.parseDOM(new BufferedReader(new InputStreamReader(resource.getInputStream(), "UTF-8")), null);
            NodeList n = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            if (logger.isDebugEnabled()) {
                printDocument(doc, n);
            }
            String value = n.item(0).getFirstChild().getNodeValue();
            value = value.replace("°C", "");
            return conversionService.convert(value, clazz);
        } catch (Exception e) {
            throw new IllegalStateException("Information extraction failed.", e);
        }
    }

    private void printDocument(Document doc, NodeList n) throws TransformerException {
        Source source = new DOMSource(doc);
        StringWriter stringWriter = new StringWriter();
        Result result = new StreamResult(stringWriter);
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        transformer.transform(source, result);
    }
}